﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using WpfDemo.Common;
using WpfDemo.IRepository;
using WpfDemo.Models;
using WpfDemo.Models.Context;

namespace WpfDemo.Repository
{
    public class BaseRepository<T> : IBaseRepository<T> where T : class
    {
        private readonly EfContext Db;
        public BaseRepository(EfContext db)
        {
            Db = db;
        }

        #region 添加数据
        public virtual bool Add(T entity)
        {
            Db.Set<T>().Add(entity);

            var flag = Db.SaveChanges() > 0;
            Db.Entry(entity).State = EntityState.Detached;
            return flag;
        }
        public virtual bool AddRange(List<T> entities)
        {
            Db.Set<T>().AddRange(entities);
            return Db.SaveChanges() > 0;
        }
        #endregion

        #region 修改数据
        public virtual bool Update(T entity)
        {
            
            if (Db.Entry(entity).State == EntityState.Detached)
                Db.Set<T>().Attach(entity);
            Db.Entry(entity).State = EntityState.Modified;
            var flag = Db.SaveChanges() > 0;
            Db.Entry(entity).State = EntityState.Detached;
            return flag;
        }
        #endregion

        #region 删除数据
        public virtual bool Delete(T entity)
        {
            if (Db.Entry(entity).State == EntityState.Detached)
                Db.Set<T>().Attach(entity);
            Db.Entry(entity).State = EntityState.Deleted;
            return Db.SaveChanges() > 0;
        }
        public virtual bool Delete(int id)
        {
            var entity = Db.Set<T>().Find(id);//如果实体已经在内存中，那么就直接从内存拿，如果内存中跟踪实体没有，那么才查询数据库。
            if (entity != null) Db.Entry(entity).State = EntityState.Deleted;
            return Db.SaveChanges() > 0;
        }

        public virtual bool BatchDelete(List<T> entities)
        {
            Db.Set<T>().RemoveRange(entities);
            return Db.SaveChanges() > 0;
        }
        public virtual bool BatchDelete(params int[] ids)
        {
            foreach (var item in ids)
            {
                var entity = Db.Set<T>().Find(item);//如果实体已经在内存中，那么就直接从内存拿，如果内存中跟踪实体没有，那么才查询数据库。
                if (entity != null) Db.Set<T>().Remove(entity);
            }
            return Db.SaveChanges() > 0;
        }
        #endregion

        #region 获取数据

        public virtual IQueryable<T> Query(bool isNoTracking = true)
        {
            return isNoTracking ? Db.Set<T>().AsNoTracking() : Db.Set<T>();
        }
        public List<T> QueryList(Expression<Func<T, bool>> whereLambda)
        {
            return Db.Set<T>().Where(whereLambda).AsNoTracking().ToList();
        }

        public T QuerySingle(Expression<Func<T, bool>> whereLambda)
        {
            return Db.Set<T>().Where(whereLambda).AsNoTracking().SingleOrDefault();
        }


        public bool Exists(Expression<Func<T, bool>> whereLambda)
        {
            return Db.Set<T>().Where(whereLambda).AsNoTracking().Any();
        }

        public List<T> QueryPageList<S>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, S>> orderbyLambda, out int total, bool isAsc = true, bool isNoTracking = true)
        {
            IQueryable<T> data = isAsc ?
               Db.Set<T>().OrderBy(orderbyLambda) :
               Db.Set<T>().OrderByDescending(orderbyLambda);

            if (whereLambda != null)
            {
                data = isNoTracking ? data.Where(whereLambda).AsNoTracking() : data.Where(whereLambda);
            }
            total = data.Count();
            return data.Skip(pageSize * (pageIndex - 1)).Take(pageSize).ToList();
        }

        public PageData<T> GetPageAsync<TKey>(PageInfo page, Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderBy, bool isAsc = true, bool isNoTracking = true)
        {
            IQueryable<T> data = isAsc ?
               Db.Set<T>().OrderBy(orderBy) :
               Db.Set<T>().OrderByDescending(orderBy);

            if (whereLambda != null)
            {
                data = isNoTracking ? data.Where(whereLambda).AsNoTracking() : data.Where(whereLambda);
            }
            var cnt = data.Count();
            page.TotalCount = cnt;
            PageData<T> pageData = new PageData<T>
            {
                TotalCount = cnt,
                Rows = data.Skip((page.pageIndex - 1) * page.pageSize).Take(page.pageSize).ToList()
            };
            return pageData;
        }


        #endregion
    }

    public static class DbContextExtensions
    {
        public static void DettachWhenExist<TEntity>(this DbContext dbContext, Func<TEntity, bool> predicate) where TEntity : class
        {
            var local = dbContext.Set<TEntity>().Local.FirstOrDefault(predicate);
            if (local is not null)
            {
                dbContext.Entry(local).State = EntityState.Detached;
            }
        }
    }
}
